#ifndef CRC_H
#define CRC_H

#include <stdint.h>
#include "NoCopy.h"
#include <visibility.h>

namespace crc {

namespace reverser {
template <typename T>
T reverse(T x);
}

template <typename T, uint32_t poly, uint32_t init, bool reverseInput, bool reverseOutput, uint32_t finalXor>
class __EXPORT Crc : NoCopy {
public:
	Crc()
		: width_(sizeof(T) * 8)
		, poly_(static_cast<T>(poly))
		, init_(static_cast<T>(init))
		, reverseInput_(reverseInput)
		, reverseOutput_(reverseOutput)
		, finalXor_(static_cast<T>(finalXor))
		, crc_(init_)
	{}

	void accumulate(uint8_t c) {
		c = reverseInput_ ? reverser::reverse(c) : c;
		crc_ ^= c << (width_ - 8);

		for (int i = 0; i < 8; ++i)
			crc_ = (crc_ & (1 << (width_ - 1))) ? (crc_ << 1) ^ poly_ : (crc_ << 1);
	}

	void accumulate(const uint8_t *buf, int n) {
		for (int i = 0; i < n; ++i)
			accumulate(buf[i]);
	}

	T get() const {
		return (reverseOutput_ ? reverser::reverse(crc_) : crc_) ^ finalXor_;
	}

	void reset() {
		crc_ = init_;
	}

	T calc(const uint8_t *buf, int n) {
		reset();
		accumulate(buf, n);
		return get();
	}

private:
	const uint8_t width_;
	const T poly_;
	const T init_;
	const bool reverseInput_;
	const bool reverseOutput_;
	const T finalXor_;
	T crc_;
};

}

#endif
